#include "qspiflash.h"

/* qqspiflash 芯片相关特性 */
qspiflash_attr s_qspiflash_attr;

/* ----------------------------- qspi 操作 ------------------------------ */

/* QSPI FLASH 写使能 */
static uint8_t QSPI_WriteEnable(void) 
{
    QSPI_CommandTypeDef     cmd  = {0};
    QSPI_AutoPollingTypeDef conf = {0};

    /* 写使能 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;    /* 1线方式发送指令 */
    cmd.Instruction     = QSPI_W25Q64JV_WRITE_ENABLE;  /* 写使能指令 */
    cmd.DummyCycles     = 0;                          /* 空周期  */

    if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) 
    {
        return HAL_ERROR;
    }

    /* 读取状态寄存器 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE,    /* 1线方式发送指令 */
    cmd.Instruction     = QSPI_W25Q64JV_STATUS_REG1;  /* 读取状态命令 */
    cmd.DataMode        = QSPI_DATA_1_LINE;           /* 1线接收数据 */
    cmd.NbData          = 1;                          /* 读取0个数据 */
    cmd.DummyCycles     = 0;                          /* 空周期  */

    /* 屏蔽位设置的bit1，匹配位等待bit0为1，即不断查询状态寄存器bit1，等待其为1 */
    conf.Match           = 0x02;
    conf.Mask            = 0x02;
    conf.MatchMode       = QSPI_MATCH_MODE_AND;
    conf.StatusBytesSize = 1;
    conf.Interval        = 0x10;
    conf.AutomaticStop   = QSPI_AUTOMATIC_STOP_ENABLE;

    if (HAL_QSPI_AutoPolling(&hqspi, &cmd, &conf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) 
    {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* 等待 QSPI Flash 就绪，主要用于Flash擦除和页编程时使用 */
static uint8_t QSPI_AutoPollingMemReady(void)
{
    QSPI_CommandTypeDef     cmd  = {0};
    QSPI_AutoPollingTypeDef conf = {0};

    /* 读取状态寄存器1 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;    /* 1线方式发送指令 */
    cmd.Instruction     = QSPI_W25Q64JV_STATUS_REG1;  /* 状态寄存器1指令 */
    cmd.DataMode        = QSPI_DATA_1_LINE;            /* 1线接收数据 */


    /* 屏蔽位设置的bit0，匹配位等待bit0为0，即不断查询状态寄存器bit0，等待其为0 */
    conf.Match           = 0x00;
    conf.Mask            = 0x01;
    conf.MatchMode       = QSPI_MATCH_MODE_AND;
    conf.StatusBytesSize = 1;
    conf.Interval        = 0x10;
    conf.AutomaticStop   = QSPI_AUTOMATIC_STOP_ENABLE;

    if (HAL_QSPI_AutoPolling(&hqspi, &cmd, &conf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) 
    {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* 读取 QSPI Flash 器件ID */
static uint32_t QSPI_ReadID(void)
{
    uint32_t uiID;
    uint8_t buf[3];
    QSPI_CommandTypeDef cmd  = {0};

    /* 读取状态寄存器1 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;    /* 1线方式发送指令 */
    cmd.Instruction     = QSPI_W25Q64JV_DEVICE_ID;    /* 读取ID命令 */
    cmd.AddressMode     = QSPI_ADDRESS_NONE;    /* 不需要发送地址 */
    cmd.DataMode        = QSPI_DATA_1_LINE;            /* 1线接收数据 */
    cmd.NbData          = 3;                    /* 读取三个数据 */
    cmd.DummyCycles     = 0;                    /* 空周期 */

    if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) 
    {
        return 0;
    }

    if (HAL_QSPI_Receive(&hqspi, buf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) 
    {
        return 0;
    }

    uiID = (buf[0] << 16) | (buf[1] << 8 ) | buf[2];

    return uiID;
}

/* --------------------------------------------------------------------- */

/* qqspiflash 初始化 */
int qspiflash_init(void)
{
    s_qspiflash_attr.chip_id = QSPI_ReadID();

    if (s_qspiflash_attr.chip_id != QSPI_W25Q64JV_FLASH_ID)
    {
        return -1;
    }

    /* 属性赋值 */
    strcpy(s_qspiflash_attr.chip_name, "W25Q64BV");
    s_qspiflash_attr.total_size  = 8 * 1024 * 1024;    /* 总容量 = 8M */
    s_qspiflash_attr.bank_size   = 64 * 1024;          /* 块大小 = 64K */
    s_qspiflash_attr.sector_size = 4 * 1024;           /* 扇区大小 = 4K */
    s_qspiflash_attr.page_size   = 256;                /* 页大小 = 256B */

    return 0;
}

/* 擦除 QSPI Flash 指定的扇区的地址 (扇区大小4KB) */
static uint8_t QSPI_EraseSector(uint32_t _uiSectorAddr)
{
    QSPI_CommandTypeDef cmd = {0};

    /* 写使能 */
    if (QSPI_WriteEnable() != HAL_OK) 
    {
        return HAL_ERROR;
    }

    /* 扇区擦除 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;     /* 1线方式发送指令 */
    cmd.Instruction     = QSPI_W25Q64JV_ERASE_SECTOR;  /* 扇区擦除 */
    cmd.AddressSize     = QSPI_ADDRESS_24_BITS;        /* 24位地址 */
    cmd.AddressMode     = QSPI_ADDRESS_1_LINE;         /* 1线地址 */
    cmd.Address         = _uiSectorAddr;        /* 扇区首地址，保证是4KB整数倍 */
    cmd.DataMode        = QSPI_DATA_NONE;       /* 无需发送数据 */
    cmd.DummyCycles     = 0;                    /* 空周期 */

    if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        return HAL_ERROR;
    }
    
    if (QSPI_AutoPollingMemReady() != HAL_OK) 
    {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/*
* 页编程，页大小256字节，任意页都可以写入
* _pBuf : 数据源缓冲区;
* _uiWriteAddr ：目标区域首地址，即页首地址，比如0， 256, 512等.
* _usWriteSize ：数据个数，不能超过页面大小，范围1 - 256.
*/
static uint8_t QSPI_WriteBuffer(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize)
{
    QSPI_CommandTypeDef cmd = {0};

    /* 写使能 */
    if (QSPI_WriteEnable() != HAL_OK) 
    {
        return HAL_ERROR;
    }

    /* 写数据配置 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;     /* 1线方式发送指令 */
    cmd.AddressSize     = QSPI_ADDRESS_24_BITS;        /* 24位地址 */
    cmd.AddressMode     = QSPI_ADDRESS_1_LINE;         /* 1线地址 */
    
/* 选择单线写, 还是4线写 */
#ifdef QSPI_W25Q64JV_SELECT_PROGRAME_1LINE
    cmd.Instruction     = QSPI_W25Q64JV_PAGE_PROGRAM_1LINE;  /* 单线写数据 */
    cmd.DataMode        = QSPI_DATA_1_LINE;                  /* 单线 */ 
#else
    cmd.Instruction     = QSPI_W25Q64JV_PAGE_PROGRAM_4LINE;  /* 4线写数据 */
    cmd.DataMode        = QSPI_DATA_4_LINES;                  /* 4线 */ 
#endif
    
    cmd.NbData          = _usWriteSize;         /* 写数据大小 */
    cmd.Address         = _uiWriteAddr;         /* 写入地址 */
    cmd.DummyCycles     = 0;                    /* 空周期 */

    if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) 
    {
        return HAL_ERROR;
    }

    if (HAL_QSPI_Transmit(&hqspi, _pBuf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) 
    {
        return HAL_ERROR;
    }

    if (QSPI_AutoPollingMemReady() != HAL_OK) 
    {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/*
* 连续读取若干字节，字节个数不能超出芯片容量
* _pBuf : 数据源缓冲区。
* _uiReadAddr ：起始地址。
* _usSize ：数据个数, 可以大于PAGE_SIZE, 但是不能超出芯片总容量
*           连续读取若干字节，字节个数不能超出芯片容量
*/
static uint8_t QSPI_ReadBuffer(uint8_t * _pBuf, uint32_t _uiReadAddr, uint32_t _uiSize)
{
    QSPI_CommandTypeDef cmd = {0};

    /* 读数据配置 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;     /* 1线方式发送指令 */
    cmd.AddressSize     = QSPI_ADDRESS_24_BITS;        /* 24位地址 */
    cmd.AddressMode     = QSPI_ADDRESS_4_LINES; /* 4线地址 */
    cmd.SIOOMode        = QSPI_SIOO_INST_EVERY_CMD;      /* 每次传输要发指令 */
    cmd.Instruction     = QSPI_W25Q64JV_FAST_IO_READ_4LINE;  /* 4线快速读数据 */
    cmd.DataMode        = QSPI_DATA_4_LINES;    /* 4线数据 */ 
    cmd.NbData          = _uiSize;              /* 读取的数据大小 */ 
    cmd.Address         = _uiReadAddr;          /* 读取数据的起始地址 */
    cmd.DummyCycles     = 4;                    /* 4个周期 */

    if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) 
    {
        return HAL_ERROR;
    }
    
    /* 读取 */
    if (HAL_QSPI_Receive(&hqspi, _pBuf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* QSPI FLASH 内存映射，地址 0x90000000 */
static uint8_t QSPI_MemoryMapped(void)
{
    QSPI_CommandTypeDef cmd                 = {0};
    QSPI_MemoryMappedTypeDef mem_mapped_cfg = {0};

    /* 数据映射配置 */
    cmd.InstructionMode    = QSPI_INSTRUCTION_1_LINE;            /* 1线方式发送指令 */
    cmd.Instruction        = QSPI_W25Q64JV_FAST_IO_READ_4LINE;   /* 4线快速读数据 */
    cmd.AddressMode        = QSPI_ADDRESS_4_LINES;               /* 4线地址 */
    cmd.Address            = 0;                                  /* 从0地址开始映射 */
    cmd.AddressSize        = QSPI_ADDRESS_24_BITS;               /* 24位地址 */
    cmd.AlternateByteMode  = QSPI_ALTERNATE_BYTES_4_LINES;
    cmd.AlternateBytesSize = QSPI_ALTERNATE_BYTES_8_BITS;
    cmd.AlternateBytes     = 0xf0; //datasheet p22
    cmd.DataMode           = QSPI_DATA_4_LINES;                   /* 4线数据 */ 
    cmd.NbData             = 0;                                   /* 读取的数据大小 */ 
    cmd.DummyCycles        = 4;                                   /* 4个周期 */
    
    /* 设置内存映射 */
    mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
    
    /* 内存映射 */
    if (HAL_QSPI_MemoryMapped(&hqspi, &cmd, &mem_mapped_cfg) != HAL_OK)
    {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* ***************** 向 mboot的数据存储 提供接口 ****************** */

/* 分配一片空间 */
static hw_addr_attr * qspiflash_op_for_mboot_take_free_space(uint32_t size);
/* 擦除这片空间 */
static HW_STATUS qspiflash_op_for_mboot_erase_space(uint32_t addr);
/* 写数据 */
static int qspiflash_op_for_mboot_put_data(hw_buf_attr * buf_attr, uint32_t addr);
/* 读数据 */
static hw_buf_attr * qspiflash_op_for_mboot_get_data(uint32_t addr, uint16_t nbr);
/* 内存映射 */
static int qspiflash_op_for_mboot_memory_mapped(void);


/* ***************** 提供给mboot的数据存储接口实现 ***************** */

#define QSPIFLASH_FREE_BANK_IDX    0                         /* 从第0个Bank开始*/
#define QSPIFLASH_FREE_ADDR        (0 * 64 * 1024)           /* 对应地址 */
#define QSPIFLASH_FREE_MAX_SIZE    (8 * 1024 * 1024)         /* 最大允许分配的空间 */

/* 空闲地址信息 */
hw_addr_attr qspiflash_free_addr;

/* 保存数据的buf */
uint8_t qspiflash_rd_buf[256], qspiflash_wr_buf[256];
hw_buf_attr qspiflash_write_buf = 
{
    .buf = qspiflash_wr_buf,
    .len = 256,
};

hw_buf_attr qspiflash_read_buf = 
{
    .buf = qspiflash_rd_buf,
    .len = 256,
};

/* qspiflash提供给 mboot的操作函数 */
qspiflash_op_for_mboot s_qspiflash_op_for_mboot = 
{
    .take_free_space  = qspiflash_op_for_mboot_take_free_space,
    .erase_space      = qspiflash_op_for_mboot_erase_space,
    .put_data         = qspiflash_op_for_mboot_put_data,
    .get_data         = qspiflash_op_for_mboot_get_data,
    .memory_mapped    = qspiflash_op_for_mboot_memory_mapped,
};

/* 分配一片空间 */
static hw_addr_attr * qspiflash_op_for_mboot_take_free_space(uint32_t size)
{
    int pnbr;

    /* 空间起始地址 */
    qspiflash_free_addr.addr = QSPIFLASH_FREE_ADDR;  /* 空闲空间的起始地址 */

    /* 按整数扇区分配 */
    pnbr = (size / s_qspiflash_attr.sector_size);

    if ((size % s_qspiflash_attr.sector_size))
    {
        pnbr++;
    }

    /* 空闲空间长度 */
    qspiflash_free_addr.len  = (pnbr * s_qspiflash_attr.sector_size);

    return &qspiflash_free_addr;
}

/* 擦除这片空间 */
static HW_STATUS qspiflash_op_for_mboot_erase_space(uint32_t addr)
{
    int      i, snbr;

    uint32_t eaddr = addr;

    /* 计算总扇区个数 */
    snbr = qspiflash_free_addr.len / s_qspiflash_attr.sector_size;

    /* 擦除这些扇区 */
    for (i = 0; i < snbr; i++)
    {
        QSPI_EraseSector(eaddr);
        eaddr += s_qspiflash_attr.sector_size;
    }

    return HW_TRUE;
}

/* 写数据 */
static int qspiflash_op_for_mboot_put_data(hw_buf_attr * buf_attr, uint32_t addr)
{
    int ret;

    memset(qspiflash_write_buf.buf, 0, qspiflash_write_buf.len);
    memcpy(qspiflash_write_buf.buf, buf_attr->buf, buf_attr->len);

    /* 按页写入数据 */
    ret = QSPI_WriteBuffer(qspiflash_write_buf.buf, addr, qspiflash_write_buf.len);

    return ret;
}

/* 读数据 */
static hw_buf_attr * qspiflash_op_for_mboot_get_data(uint32_t addr, uint16_t nbr)
{
    /* 清空 */
    memset(qspiflash_read_buf.buf, 0, qspiflash_read_buf.len);

    /* 按页读出数据 */
    if (QSPI_ReadBuffer(qspiflash_read_buf.buf, addr, qspiflash_read_buf.len) != HAL_OK)
    {
        return NULL;
    }

    return &qspiflash_read_buf;
}

/* 内存映射 */
static int qspiflash_op_for_mboot_memory_mapped(void)
{
    uint8_t ret;

    /* QSPI FLASH 内存映射，地址 0x90000000 */
    ret = QSPI_MemoryMapped();

    if (ret != HAL_OK)
    {
        return -1;
    }

    return 0;
}

/* ***************** 向 mboot的数据存储 提供接口和接收配置信息 ***************** */

qspiflash_op_for_mboot *qspiflash_op_for_mboot_register(void)
{
    /* 返回操作函数接口 */
    return &s_qspiflash_op_for_mboot;
}
